#pragma once

#include <Eigen/Dense>
#include <Eigen/Sparse>
#include <vector>
#include "particle2realflowUtil/slVector.H"
#include "particle2realflowUtil/kdTree.H"
#include <string>

#define LeastAffectiveNeighbors 10
#define LeastAffectiveParticles 5

using namespace std;
using namespace Eigen;

struct arguConfigure {
	int fileType;	//1 represents for .txt file; 2 represents for .obj file; 3 represents for .off file
	std::string workDirectory;
	std::string fileName;	//the url of the file name , no including suffix such as .txt

	double h;	//grid cell length
	double LaplacianWeight; 
	double VerticesWeight;

	int pcaMaxNumParticleNeighbors;
	int pcaMaxNumVerticesNeighbors;
	double pcaParticleRadiusRate;
	double pcaVerticesRadiusRate;

	double LaplacianWeightRate;

	arguConfigure() {
		fileType = 0;
		workDirectory = "";
		fileName = "";
		h = 0.01f;
		LaplacianWeight = 0.005;
		VerticesWeight = 100.0;
		pcaMaxNumParticleNeighbors = 200;
		pcaMaxNumVerticesNeighbors = 50;
		pcaParticleRadiusRate = pcaVerticesRadiusRate = 2.0;
		LaplacianWeightRate = 5.0;
	}
};



/*********************************    output some Information      ***************************************/

// output basic infomation of model
void outputModelinfo(const Eigen::MatrixXd Vertices, const Eigen::MatrixXi Faces);

// output the length of the diagonal of the bounding box
void outputModelLength(const Eigen::MatrixXd & Vertices);

/********************************************************************************************************/



/************************************    Math Functions     ********************************************/

// solve linear equations
void solveLinearLeastEquation(const Eigen::SparseMatrix<double > & LeftM, const Eigen::MatrixXd & RightM, 
	Eigen::MatrixXd & newVertices);

// set Laplacian Matrix Weight from a Vector
void getWeightMatrix(const Eigen::VectorXd & weightVec, double originUniformWeight, double LaplacianWeightRate,
	Eigen::SparseMatrix<double, RowMajor> & weightM);

/********************************************************************************************************/



/*************************************    I/O Data      *************************************************/

// read configure file
void readConfigureFile(std::string configureURL, arguConfigure & argumentInfo);

// read model file (.off / .obj)
void readModelFile(std::string & filename, Eigen::MatrixXd & Vertices, Eigen::MatrixXi & Faces);

// Write mesh into .obj file
void writeIntoOBJ(std::string filename, const Eigen::MatrixXd & Vertices, const Eigen::MatrixXi & Faces);

// read particle data
void readParticlData(std::string filename, std::vector<SlVector3>& Particles);

/********************************************************************************************************/



/*********************************    Particle Processing      ***************************************/

// use particle data to construct a mesh storing in V&F ;
void SkinningParticles(const char *infname, float shrink, Eigen::MatrixXd& V, Eigen::MatrixXi& F,
	std::vector<SlVector3>& Particles, arguConfigure & gridInfo);

// calculate Anisotropy of each particles
void calculateParticleAnisotropy(const std::vector<SlVector3> & Particles, KDTree & particleKDTree,
	int num, double radius, Eigen::MatrixXd & ParticleAnisotropy);

/********************************************************************************************************/


/***********************************    Mesh Processing      *******************************************/

// Compute centroid and subtract, scale it to uniformed scale
void meshScale(Eigen::MatrixXd & Vertices, Eigen::MatrixXi & Faces);

//  Compute integral of Gaussian curvature
void getGussianCurvature(const Eigen::MatrixXd & Vertices, const Eigen::MatrixXi & Faces,
							Eigen::VectorXd &GussianCurvature);

//  Compute integral of Laplacian Matrix
void getLaplacianMatrix(const Eigen::MatrixXd & Vertices, const Eigen::MatrixXi & Faces,
						Eigen::SparseMatrix<double> & LaplaceMatrix);

// calculate the offset of each vertices of the mesh
void calculateMeshAnisotropy(const Eigen::MatrixXd & Vertices, const std::vector<SlVector3> & Particles,
						KDTree & particleKDTree, int num, double radius, const Eigen::MatrixXd & ParticleAnisotropy,
						Eigen::MatrixXd & VerticesAnisotropy, Eigen::VectorXi &numPointsOfPerVer);

/*******************************************************************************************************/